package vip.xiaonuo.auth.modular.wechat.service.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import vip.xiaonuo.auth.api.SaBaseLoginUserApi;
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
import vip.xiaonuo.auth.modular.login.enums.AuthExceptionEnum;
import vip.xiaonuo.auth.modular.wechat.config.WechatConfig;
import vip.xiaonuo.auth.modular.wechat.enums.QrCodeStatus;
import vip.xiaonuo.auth.modular.wechat.service.AuthWechatService;
import vip.xiaonuo.common.cache.CommonCacheOperator;
import vip.xiaonuo.common.consts.CacheConstant;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.tenant.TenantContext;
import vip.xiaonuo.sys.api.WechatApi;
import vip.xiaonuo.sys.api.dto.WechatLoginCallbackDto;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @author zjt
 * @description
 * @date 2025/7/17 11:23
 */
@Service
@RequiredArgsConstructor
public class AuthWechatServiceImpl implements AuthWechatService {

    private final CommonCacheOperator cacheOperator;

    private final WechatConfig wechatConfig;

    @Resource(name = "loginUserApi")
    private SaBaseLoginUserApi loginUserApi;

    private final WechatApi wechatApi;

    private static final String STATE_PREFIX = "state:";

    /**
     * 获取登录的Url
     *
     * @param state
     * @return
     */
    @Override
    public String getOAuthUrl(String state) {
        cacheOperator.setCatchString(STATE_PREFIX + state, "0", 305);
        return String.format("https://open.weixin.qq.com/connect/oauth2/authorize?" +
                        "appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_privateinfo" +
                        "&agentid=%s&state=%s&#wechat_redirect",
                wechatConfig.getCorpId(),
                URLEncoder.encode(wechatConfig.getRedirectUri(), StandardCharsets.UTF_8),
                wechatConfig.getAgentId(),
                state);
    }

    /**
     * 检查登录状态
     *
     * @param state
     * @return
     */
    @Override
    public String checkScanStatus(String state) {
        return cacheOperator.getCatchString(STATE_PREFIX + state);
    }

    /**
     * 企业微信登录
     *
     * @param code
     * @param state
     * @return
     */
    @Override
    public void doLogin(String code, String state) {
        String[] stateArr = state.split("_");
        if (stateArr.length == 2) {
            checkTime(stateArr[1]);
        } else {
            throw new CommonException("参数不合法");
        }
        //保存到Redis
        if (cacheOperator.compareAndSet(STATE_PREFIX + state, "0", QrCodeStatus.SCANNED.getStatus(), 300)) {
            //执行登录流程
            TenantContext.ignoreHandle(() -> {
                WechatLoginCallbackDto loginCallbackDto = wechatApi.getWechatCallbackByLoginCode(code);
                SaBaseLoginUser saBaseLoginUser = loginUserApi.getUserByEmpNo(loginCallbackDto.getUserid());
                if (ObjectUtil.isEmpty(saBaseLoginUser)) {
                    throw new CommonException(AuthExceptionEnum.ACCOUNT_ERROR.getValue());
                }
                // 执行B端登录
                if (!saBaseLoginUser.getEnabled()) {
                    throw new CommonException(AuthExceptionEnum.ACCOUNT_DISABLED.getValue());
                }
                // 执行登录
                StpUtil.login(saBaseLoginUser.getId(), new SaLoginModel().setExtra("name", saBaseLoginUser.getName()));
                StpUtil.getSession().set("tenantId", saBaseLoginUser.getTenantId());
                // 填充B端用户信息并更新缓存
                fillSaBaseLoginUserAndUpdateCache(saBaseLoginUser);
                cacheOperator.setCatchString(STATE_PREFIX + state, StpUtil.getTokenInfo().tokenValue, 300);
                if (StringUtils.isNotBlank(loginCallbackDto.getUser_ticket())) {
                    Thread.startVirtualThread(() -> TenantContext.ignoreHandle(() -> wechatApi.saveSensitiveData(loginCallbackDto.getUser_ticket(), saBaseLoginUser.getId())));
                }
            });
        } else {
            throw new CommonException("二维码已过期");
        }
    }

    private boolean checkTime(String timeStamp) {
        try {
            long timestamp = Long.parseLong(timeStamp);
            long currentTime = System.currentTimeMillis();
            long fiveMinutesInMillis = 5 * 60 * 1000;
            if (currentTime - timestamp <= fiveMinutesInMillis) {
                return true;
            } else {
                throw new CommonException("二维码已过期");
            }
        } catch (NumberFormatException e) {
            throw new CommonException("参数不合法");
        }
    }

    /**
     * 填充B端用户信息并更新缓存
     *
     * @author xuyuxiang
     * @date 2024/7/22 22:00
     */
    private void fillSaBaseLoginUserAndUpdateCache(SaBaseLoginUser saBaseLoginUser) {
        // 角色集合
        List<JSONObject> roleList = loginUserApi.getRoleListByUserId(saBaseLoginUser.getId());
        // 角色id集合
        List<String> roleIdList = roleList.stream().map(jsonObject -> jsonObject.getStr("id")).toList();
        // 角色码集合
        List<String> roleCodeList = roleList.stream().map(jsonObject -> jsonObject.getStr("code")).toList();
        // 角色id和用户id集合
        List<String> userAndRoleIdList = CollectionUtil.unionAll(roleIdList, CollectionUtil.newArrayList(saBaseLoginUser.getId()));
        // 获取按钮码
        saBaseLoginUser.setButtonCodeList(loginUserApi.getButtonCodeListListByUserAndRoleIdList(userAndRoleIdList));
        // 获取移动端按钮码
        saBaseLoginUser.setMobileButtonCodeList(loginUserApi.getMobileButtonCodeListListByUserIdAndRoleIdList(userAndRoleIdList));
        // 获取数据范围
        saBaseLoginUser.setDataScopeList(Convert.toList(SaBaseLoginUser.DataScope.class, loginUserApi.getPermissionListByUserIdAndRoleIdList(userAndRoleIdList, saBaseLoginUser.getOrgId())));
        // 获取权限码
        List<String> permissionCodeList = saBaseLoginUser.getDataScopeList().stream().map(SaBaseLoginUser.DataScope::getApiUrl).toList();
        // 设置权限码
        saBaseLoginUser.setPermissionCodeList(permissionCodeList);
        // 权限码列表存入缓存
        cacheOperator.put(CacheConstant.AUTH_B_PERMISSION_LIST_CACHE_KEY + saBaseLoginUser.getId(), permissionCodeList);
        // 获取角色码
        saBaseLoginUser.setRoleCodeList(roleCodeList);
        // 缓存用户信息，此处使用TokenSession为了指定时间内无操作则自动下线
        StpUtil.getTokenSession().set("loginUser", saBaseLoginUser);
    }

}
